package simplification;

import antlr.*;
import antlr.collections.AST;

import java.util.*;

import ptolemy.actor.*;

import db.*;
import FlowGraph.*;
import SymbolTable.*;
import FlowGraph.SSA.*;
import JavaGrammar.*;
import FlowGraph.Nodes.*;
import FlowGraph.StatementInfo.*;
import JavaGrammar.TreeScanners.ConstantFolding;
import util.*;

//////////////////////////////////////////////////////////////////////////
//// Simplification
/**
        Controla o processo de simplificacao. A primeira fase e independente
de um modelo computacional.

        O algoritmo de deadcode nao elimina os nos. Apenas marca alguns
como deleted. Dependendo da construcao, isso determinados nos estao
deleted.

@author Ivan Jeukens
@version v 0.1 13/05/2003
*/
public class Simplification {
    
    public Simplification() {
        
    }

    ///////////////////////////////////////////////////////////////////
    ////                         public methods                    ////

    public static boolean constantValue(AST ast) {
        if(ast.getType() == JavaTokenTypes.NUM_INT ||
           ast.getType() == JavaTokenTypes.CHAR_LITERAL ||
           ast.getType() == JavaTokenTypes.STRING_LITERAL ||
           ast.getType() == JavaTokenTypes.NUM_FLOAT ||
           ast.getType() == JavaTokenTypes.NUM_DOUBLE ||
           ast.getType() == JavaTokenTypes.NUM_LONG ||
           ast.getText().equals("this") ||
           ast.getText().equals("true") ||
           ast.getText().equals("false") ||
           ast.getText().equals("null"))
            return true;

        return false;
    }

    public void simplify(ClassData cdata) {
        _simplify(cdata);
    }

    ///////////////////////////////////////////////////////////////////
    ////                        private methods                    ////

    private void _simplify(ClassData cdata) {
        Iterator ite = cdata.getMethods().getElements().iterator();
        while(ite.hasNext()) {
            LinkedList l = (LinkedList) ite.next();
            for(int i = 0;i < l.size();i++) {
                STMethodEntry me = (STMethodEntry) l.get(i);
                FlowGraph fg = me.getFlowGraph();
                if(fg.isBuild()) {
                    _oneRound(fg, cdata);
                }
            }
        }
    }

    private void _oneRound(FlowGraph fg, ClassData cinfo) {
        _subsStaticFinalVars(fg, cinfo);
        _constantPropagation(fg, cinfo);
        _deadCodeElimination(fg, cinfo);
    }

    // TEM UM BUG!! Fode quando tem um literal que esta em notacao cientifica!
    private void _subsStaticFinalVars(FlowGraph fg, ClassData cinfo) {
        SSA ssa = fg.getSSA();
        Iterator ite = cinfo.variablesIterator();
        while(ite.hasNext()) {
            STVariableEntry var = (STVariableEntry) ite.next();
            if(var.isStatic() && var.isFinal() && !var.isArray()) {
                AST ast = var.getNode();
                ast = ast.getFirstChild();
                ast = ast.getNextSibling();
                ast = ast.getNextSibling();
                ast = ast.getNextSibling();
                if(ast != null) {    // tem assign
                    ast = ast.getFirstChild();
                    ast = ast.getFirstChild(); // esse e o valor

                    VariableInfo vinfo = ssa.getVarInfo(var);
                    LinkedList uses = vinfo.getUseFromChain(0);
                    if(uses == null) continue;

                    for(int i = 0;i < uses.size();i++) {
                        VariableLocation loc = (VariableLocation) uses.get(i);
                        AST useAST = loc.getASTNode();
                        useAST.setText(ast.getText());
                        useAST.setType(ast.getType());

                        FlowGraphNode fgnode = loc.getFGNode();
                        if(!loc.isPhiNode()) {
                            StatementInfo info = loc.getStatementInfo();
                            info.removeIndexedVarUse(0, loc, vinfo);
                        }
                    }
                    // Nao existe mais esse index.
                    vinfo.removeIndex(0);
                }
            }
        }
    }

    private void _constantPropagation(FlowGraph fg, ClassData cinfo) {
        // Vou processar em topological order. Assim, nao preciso me preocupar
        // em propagar o efeito de uma constante.
        LinkedList to = fg.getTopologicalOrder(false);
        ConstantFolding folding = new ConstantFolding();
        for(int i = 0;i < to.size();i++) {
            FlowGraphNode fgnode = (FlowGraphNode) to.get(i);

            if(_hasSideEffects(fgnode)) continue;

            // primeiro olha os phi node na cadeca desse no.
            int j = 0;
            int total = fgnode.phiNodeSize();
            while(j < total) {
                PhiNode phinode = fgnode.getPhiNode(j);
                if(_isConstantPhiNode(phinode.getAST(), fgnode, fg, cinfo)) {
                    fgnode.removePhiNode(phinode);
                    total--;
                }
                else {
                    j++;
                }
            }

            switch(fgnode.getType()) {
                case FlowGraphNode.BASIC_BLOCK: {
                    BasicBlockNode bb = (BasicBlockNode) fgnode;
                    j = 0;
                    total = bb.statementsSize();
                    while(j < total) {
                        AST ast = bb.getStatement(j);

                        folding.reset();
                        try {
                            folding.statement(ast);
                        }
                        catch(RecognitionException ex) {
                            ex.printStackTrace();
                        }

                        if(_isConstantAssigment(ast, bb, fg, cinfo)) {
                            bb.removeStatement(j);
                            total--;
                        }
                        else {
                            j++;
                        }
                    }
                } break;

                case FlowGraphNode.METHOD_CALL: {
                    MethodCallNode mcnode = (MethodCallNode) fgnode;
                    AST ast = mcnode.getAbsTreeNode();

                    folding.reset();
                    try {
                        folding.statement(ast);
                    }
                    catch(RecognitionException ex) {
                        ex.printStackTrace();
                    }
                } break;

                case FlowGraphNode.DO_CONDITION: {
                    DoConditionNode dcond = (DoConditionNode) fgnode;
                    AST cond = dcond.getCondition();

                    folding.reset();
                    try {
                        folding.statement(cond);
                    }
                    catch(RecognitionException ex) {
                        ex.printStackTrace();
                    }
                } break;

                case FlowGraphNode.FOR_BEGIN: {
                    ForBeginNode fbeg = (ForBeginNode) fgnode;
                    AST cond = fbeg.getCondition();

                    folding.reset();
                    try {
                        folding.statement(cond.getFirstChild());
                    }
                    catch(RecognitionException ex) {
                        ex.printStackTrace();
                    }
                } break;

                case FlowGraphNode.IF_BEGIN: {
                    IfBeginNode beg = (IfBeginNode) fgnode;
                    AST cond = beg.getCondition();

                    folding.reset();
                    try {
                        folding.statement(cond);
                    }
                    catch(RecognitionException ex) {
                        ex.printStackTrace();
                    }
                } break;

                case FlowGraphNode.RETURN: {
                    ReturnNode retn = (ReturnNode) fgnode;
                    AST expr = retn.getExpression();
                    if(expr != null) {
                        folding.reset();
                        try {
                            folding.statement(expr);
                        }
                        catch(RecognitionException ex) {
                            ex.printStackTrace();
                        }
                    }
                } break;
                
                case FlowGraphNode.SWITCH_BEGIN: {
                    SwitchBeginNode swbeg = (SwitchBeginNode) fgnode;
                    AST expr = swbeg.getExpression();

                    folding.reset();
                    try {
                        folding.statement(expr);
                    }
                    catch(RecognitionException ex) {
                        ex.printStackTrace();
                    }
                } break;

                case FlowGraphNode.WHILE_BEGIN: {
                    WhileBeginNode beg = (WhileBeginNode) fgnode;
                    AST cond = beg.getCondition();

                    folding.reset();
                    try {
                        folding.statement(cond);
                    }
                    catch(RecognitionException ex) {
                        ex.printStackTrace();
                    }
                } break;
            }
        }
    }

    // Verifica se esse phi node so tem constantes, e do mesmo valor
    private boolean _isConstantPhiNode(AST ast, FlowGraphNode node,
            FlowGraph fg, ClassData info) {
        SSA ssa = fg.getSSA();
        if(ast.getType() == JavaTokenTypes.EXPR) {
            AST v = ast.getFirstChild();
            if(v.getType() == JavaTokenTypes.ASSIGN) {
                AST var = v.getFirstChild();
                AST cte = var.getNextSibling();

                // Variavel local
                STEntry entry = node.getVar(SSA.getVarName(var.getText()));
                if(entry != null && !entry.isArray()) {
                    return _checkConstantPhiNode(var, cte, entry, ssa);
                }

                // Variavel da classe
                entry = info.getVariable(var.getText());
                if(entry != null && !entry.isArray()) {
                    return _checkConstantPhiNode(var, cte, entry, ssa);
                }

                // Parametro
                entry = info.getParameter(var.getText());
                if(entry != null && !entry.isArray()) {
                    return _checkConstantPhiNode(var, cte, entry, ssa);
                }

                // Porto
                entry = info.getParameter(var.getText());
                if(entry != null && !entry.isArray()) {
                    return _checkConstantPhiNode(var, cte, entry, ssa);
                }
            }
        }
        return false;
    }

    // Verifica se a ast e var = cte. Var nao pode ser array.
    // Propaga as constantes se for o caso e atualiza a variableInfo
    private boolean _isConstantAssigment(AST ast, FlowGraphNode node,
            FlowGraph fg, ClassData info) {
        SSA ssa = fg.getSSA();
        if(ast.getType() == JavaTokenTypes.EXPR) {
            AST v = ast.getFirstChild();
            if(v.getType() == JavaTokenTypes.ASSIGN) {
                AST var = v.getFirstChild();
                AST cte = var.getNextSibling();

                // Variavel local
                if(var.getType() == JavaTokenTypes.INDEX_OP) return false;
                STEntry entry = node.getVar(SSA.getVarName(var.getText()));
                if(entry != null && !entry.isArray()) {
                    return _checkConstantExpressions(var, cte, entry, ssa, false);
                }

                // Variavel da classe
                entry = info.getVariable(var.getText());
                if(entry != null && !entry.isArray()) {
                    return _checkConstantExpressions(var, cte, entry, ssa, true);
                }

                // Parametro
                entry = info.getParameter(var.getText());
                if(entry != null && !entry.isArray()) {
                    return _checkConstantExpressions(var, cte, entry, ssa, true);
                }

                // Porto
                entry = info.getParameter(var.getText());
                if(entry != null && !entry.isArray()) {
                    return _checkConstantExpressions(var, cte, entry, ssa, true);
                }
            }
        }
        else
        if(ast.getType() == JavaTokenTypes.VARIABLE_DEF) {
            AST v = ast.getFirstChild();
            v = v.getNextSibling();
            AST var = v.getNextSibling();
            v = var.getNextSibling();
            if(v != null) {
                v = v.getFirstChild();
                if(v.getType() == JavaTokenTypes.EXPR) {
                    AST cte = v.getFirstChild();

                    if(var.getType() == JavaTokenTypes.INDEX_OP) return false;
                    // Variavel local
                    STEntry entry = node.getVar(SSA.getVarName(var.getText()));
                    if(entry != null && !entry.isArray()) {
                        return _checkConstantExpressions(var, cte, entry, 
                           ssa, false);
                    }

                    // Variavel da classe
                    entry = info.getVariable(var.getText());
                    if(entry != null && !entry.isArray()) {
                        return _checkConstantExpressions(var, cte, entry, 
                            ssa, true);
                    }

                    // Parametro
                    entry = info.getParameter(var.getText());
                    if(entry != null && !entry.isArray()) {
                        return _checkConstantExpressions(var, cte, entry, 
                            ssa, true);
                    }

                    // Porto
                    entry = info.getParameter(var.getText());
                    if(entry != null && !entry.isArray()) {
                        return _checkConstantExpressions(var, cte, entry, 
                            ssa, true);
                    }
                }
            }
        }

        return false;
    }

    // Determina se e um phi node com todos os valores constantes e iguais
    private boolean _checkConstantPhiNode(AST var, AST cte, STEntry entry,
            SSA ssa) {
        AST ast = cte;
        boolean constant = true;
        String lastvalue = null;
        while(ast != null) {
            if(!Simplification.constantValue(ast)) {
                return false;
            }
            if(lastvalue == null) {
                lastvalue = ast.getText();
            }
            else {
                if(!lastvalue.equals(ast.getText())) {
                    return false;
                }
            }
            ast = ast.getNextSibling();
        }
        VariableInfo vinfo = ssa.getVarInfo(entry);
        int index = SSA.getVarIndex(var.getText());

        LinkedList uses = vinfo.getUseFromChain(index);
        if(uses == null) return false;

        // substitui as uses pela constante, e atualiza os respectivos
        // statementInfo
        for(int i = 0;i < uses.size();i++) {
            VariableLocation loc = (VariableLocation) uses.get(i);
            AST useAST = loc.getASTNode();
            useAST.setText(cte.getText());
            useAST.setType(cte.getType());
            if(!loc.isPhiNode()) {
                StatementInfo info = loc.getStatementInfo();
                info.removeIndexedVarUse(index, loc, vinfo);
            }
        }
        // Nao existe mais esse index.
        vinfo.removeIndex(index);

        return true;
    }

    // Verifica se a AST e uma dos tres tipos de expressoes constantes : um
    // literal, uma expressao so com literais e uma variavel
    private boolean _checkConstantExpressions(AST var, AST cte, STEntry entry,
            SSA ssa, boolean hasSideEffect) {
        if(_isConstant(cte)) {
            VariableInfo vinfo = ssa.getVarInfo(entry);
            int index = SSA.getVarIndex(var.getText());

            LinkedList uses = vinfo.getUseFromChain(index);
            if(uses == null) return false;

            for(int i = 0;i < uses.size();i++) {
                VariableLocation loc = (VariableLocation) uses.get(i);
                AST useAST = loc.getASTNode();
                useAST.setText(cte.getText());
                useAST.setType(cte.getType());

                // O destino da constante pode ser um phi node. Neste caso,.
                // nao tem statement info.
                if(!loc.isPhiNode()) {
                    FlowGraphNode fgnode = loc.getFGNode();
                    StatementInfo info = loc.getStatementInfo();
                    info.removeIndexedVarUse(index, loc, vinfo);
                }
            }

            // Nao existe mais esse index.
            vinfo.removeIndex(index);

            if(!hasSideEffect) { // Nao remove o statement se for uma var da classe
                return true;
            }
            return false;
        }
        else
        if(_isVariable(cte)) {

        }

        return false;
    }

    // Constant propagation
    private boolean _isConstant(AST ast) {
        // Nao pode ter filho nem sibling...
        AST v = ast.getFirstChild();
        if(v != null) return false;
        v = ast.getNextSibling();
        if(v != null) return false;

        return Simplification.constantValue(ast);
    }    

    // Copy propagation
    private boolean _isVariable(AST ast) {
        return false;
    }

    private void _deadCodeElimination(FlowGraph fg, ClassData cinfo) {
        LinkedList toprocess = new LinkedList();
        Iterator ite = fg.nodesIterator();
        while(ite.hasNext()) {
            FlowGraphNode fgnode = (FlowGraphNode) ite.next();
            _deadCode(fgnode, cinfo, fg, toprocess);
        }

        while(toprocess.size() > 0) {
            FlowGraphNode fgnode = (FlowGraphNode) toprocess.remove(0);
            _deadCode(fgnode, cinfo, fg, toprocess);
        }
    }

    private void _deadCode(FlowGraphNode fgnode, ClassData cinfo, FlowGraph fg, 
            LinkedList toprocess) {
        if(_hasSideEffects(fgnode) || fgnode.isDeleted()) {
            return;
        }

        // primeiro olha os phi node na cadeca desse no.
        int j = 0;
        int total = fgnode.phiNodeSize();
        while(j < total) {
            PhiNode phinode = fgnode.getPhiNode(j);
            if(_isDeadPhiNode(phinode)) {
                fgnode.removePhiNode(phinode);
                total--;
            }
            else {
                j++;
            }
        }

        switch(fgnode.getType()) {
            case FlowGraphNode.BASIC_BLOCK: {
                BasicBlockNode bb = (BasicBlockNode) fgnode;
                j = bb.statementsSize() - 1;
                while(j >= 0) {
                    AST ast = bb.getStatement(j);
                    StatementInfo sinfo = bb.getStatementInfo(j);
                    if(_deadAssignment(ast, bb, sinfo, fg, cinfo, 
                         toprocess)) {
                        bb.removeStatement(j);
                    }
                    j--;
                }
                
                if(bb.statementsSize() == 0 && bb.phiNodeSize() == 0) {
                    //bb.setDeleted();
                }

            } break;

            case FlowGraphNode.METHOD_CALL: {
                // Nao elimina method calls porque pode ter side effect.

                /*
                MethodCallNode mcnode = (MethodCallNode) fgnode;
                AST ast = mcnode.getAbsTreeNode();
                StatementInfo sinfo = mcnode.getStatementInfo();
                if(_deadAssignment(ast, mcnode, sinfo, fg, cinfo,
                     toprocess)) {
                    mcnode.setDeleted();
                }*/
            } break;

            case FlowGraphNode.DO_CONDITION: {
                DoConditionNode dcond = (DoConditionNode) fgnode;

                // aqui eu poderia remover quando a condicao e false,
                // e nao tem arestas nos nos do bloco que saem desse
                // escopo.
            } break;
    
            case FlowGraphNode.FOR_BEGIN: {
                ForBeginNode fbeg = (ForBeginNode) fgnode;
                
                // poderia verificar se o for tem rep = 0. Vou
                // fazer isso depois.
            } break;
    
            case FlowGraphNode.IF_BEGIN: {
                // mata o if. Coloca o no cabeca, fim e o branch nao taken 
                // como deleted
                IfBeginNode beg = (IfBeginNode) fgnode;
                AST condition = beg.getCondition();
                condition = condition.getFirstChild();
                if(condition.getText().equals("true")) {
                    beg.setDeleted();
                    FlowGraphEdge edge = beg.getElseEdge();
                    edge.setDeleted();

                    IfEndNode ifend = beg.getIfEnd();
                    ifend.setDeleted();
                    edge = ifend.getElseEdge();
                    if(edge != null) {           // pode ter um return
                        edge.setDeleted();
                    }
                    else {
                        FlowGraphNode bs = beg.getBreakScopeElse();
                        if(bs != null) {
                            edge = bs.getOutputEdge(0);
                            edge.setDeleted();
                        }
                    }
                }
                else
                if(condition.getText().equals("false")) {
                    beg.setDeleted();
                    FlowGraphEdge edge = beg.getThenEdge();
                    edge.setDeleted();

                    IfEndNode ifend = beg.getIfEnd();
                    ifend.setDeleted();
                    edge = ifend.getThenEdge();
                    if(edge != null) {          // pode ter um return
                        edge.setDeleted();
                    }
                    else {
                        FlowGraphNode bs = beg.getBreakScopeThen();
                        if(bs != null) {
                            edge = bs.getOutputEdge(0);
                            edge.setDeleted();
                        }
                    }
                }
            } break;
    
            case FlowGraphNode.SWITCH_BEGIN: {
                SwitchBeginNode swbeg = (SwitchBeginNode) fgnode;
                AST expr = swbeg.getExpression();
                int index = swbeg.hasCaseExpr(expr);
                if(index != -1) {
                    FlowGraphNode swout = (FlowGraphNode) swbeg.getOutputNode(index);
                    FlowGraphEdge edge = swbeg.getOutputEdge(swout);
                    for(int k = 0;k < swbeg.outputSize();k++) {
                        FlowGraphEdge outedge = swbeg.getOutputEdge(k);
                        if(outedge != edge) {
                            outedge.setDeleted();
                        }
                    }

                    swbeg.setDeleted();
                    SwitchEndNode swend = swbeg.getSwitchEnd();
                    swend.setDeleted();

                    FlowGraphEdge lastedge = swbeg.getLastEdge(swout);
                    for(int k = 0;k < swbeg.outputSize();k++) {
                        FlowGraphNode cend = swbeg.getCaseEndNode(k);
                        FlowGraphNode firstnode = swbeg.getOutputNode(k);
                        FlowGraphEdge outedge = swbeg.getOutputEdge(firstnode);

                        if(!outedge.isDeleted()) continue;
                        edge = swbeg.getCaseEndEdge(firstnode, cend);
                        if(edge != lastedge) {
                            edge.setDeleted();
                        }
                    }
                }
            } break;

            case FlowGraphNode.WHILE_BEGIN: {
                // mata o while. Se a condicao for false, entao elimina
                // tudo, indicando o no cabeca como false
                WhileBeginNode beg = (WhileBeginNode) fgnode;
                AST condition = beg.getCondition();
                condition = condition.getFirstChild();

                if(condition.getText().equals("false")) {
                    beg.setDeleted();
                    FlowGraphEdge edge = beg.getContinueEdge();
                    edge.setDeleted();
                }
            } break;
        }
    }

    private boolean _deadAssignment(AST ast, FlowGraphNode node,
            StatementInfo statinfo, FlowGraph fg, ClassData info,
            LinkedList toprocess) {
        if(ast.getType() == JavaTokenTypes.EXPR) {
            AST v = ast.getFirstChild();
            if(v.getType() == JavaTokenTypes.ASSIGN) {
                AST var = v.getFirstChild();
                return _checkDead(var, node, statinfo, fg, info, toprocess);
            }
        }
        else
        if(ast.getType() == JavaTokenTypes.VARIABLE_DEF) {
            AST v = ast.getFirstChild();
            v = v.getNextSibling();
            AST var = v.getNextSibling();
            if(var.getType() == JavaTokenTypes.LBRACK) return false;
            return _checkDead(var, node, statinfo, fg, info, toprocess);
        }
        return false;
    }

    private boolean _checkDead(AST var, FlowGraphNode node,
            StatementInfo statinfo, FlowGraph fg, ClassData info,
            LinkedList toprocess) {
        SSA ssa = fg.getSSA();
        // se a variavel for composta, var vai ter um .. Vai dar
        // null. Nao importa, porque nao considero variaveis compostas

        // e uma atribuicao a um array
        if(var.getType() == JavaTokenTypes.INDEX_OP) return false;
        STEntry entry = _getVarEntry(var, node, info);
        if(entry == null || entry.isArray()) return false;
        if(!(entry instanceof STLocalVariableEntry)) return false;

        VariableInfo vinfo = ssa.getVarInfo(entry);
        int index = SSA.getVarIndex(var.getText());

        LinkedList uses = vinfo.getUseFromChain(index);  // ok, nao e usado
        if(uses == null || uses.size() == 0) {
            // Entao pode apagar, e pra todo use que ta nesse
            // def, pode retirar esse use.
            Iterator ite = statinfo.useIndexedVarIterator();
            while(ite.hasNext()) {
                IndexedVarInfo ivi = (IndexedVarInfo) ite.next();
                ivi.varinfo.removeUseIndex(ivi.index, ivi.loc);

                FlowGraphNode ln = ivi.loc.getFGNode();
                if(ln != null && ln != node) {
                    toprocess.add(ln);
                }
            }
            vinfo.removeIndex(index);
            // Nao precisa mexer no statementinfo, porque ele ja
            // vai desaparecer...se nao for variavel da classe
            return true;
        }
        return false;
    }

    private boolean _isDeadPhiNode(PhiNode phinode) {
        VariableInfo vinfo = phinode.getVariableInfo();
        AST ast = phinode.getDefLabel();

        int index = SSA.getVarIndex(ast.getText());
        LinkedList uses = vinfo.getUseFromChain(index);
        if(uses == null || uses.size() == 0) {
            for(int i = 0;i < phinode.inputSize();i++) {
                AST useast = phinode.getLabel(i);
                // tem que verificar se essa ast nao e uma constante
                if(useast.getType() != JavaTokenTypes.IDENT) continue;
                vinfo.removePhiNodeUseIndex(useast);
            }
            vinfo.removeIndex(index);
            // logo esse phinode sera eliminado

            return true;
        }

        return false;
    }

    // se esse no tem aresta de exception, entao nao pode ser eliminado
    private boolean _hasSideEffects(FlowGraphNode fgnode) {
        Iterator ite = fgnode.inputEdgesIterator();
        while(ite.hasNext()) {
            FlowGraphEdge edge = (FlowGraphEdge) ite.next();
            if(edge.isException()) return true;
        }

        ite = fgnode.outputEdgesIterator();
        while(ite.hasNext()) {
            FlowGraphEdge edge = (FlowGraphEdge) ite.next();
            if(edge.isException()) return true;
        }

        return false;
    }

    private STEntry _getVarEntry(AST var, FlowGraphNode node, ClassData info) {
        String varname = SSA.getVarName(var.getText());
        STEntry entry = node.getVar(varname);
        if(entry != null) return entry;

        entry = info.getPort(varname);
        if(entry != null) return entry;

        entry = info.getParameter(varname);
        if(entry != null) return entry;

        entry = info.getVariable(varname);
        return entry;
    }

    ///////////////////////////////////////////////////////////////////
    ////                      private variables                    ////

    private LinkedList _mocs;
}
